package org.bees.cms.service.bean;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.bees.cms.common.Generic;
import org.bees.cms.common.Mappers;
import org.bees.cms.common.Order;
import org.bees.cms.common.Page;
import org.bees.cms.common.jpql.Criterion;
import org.bees.cms.common.jpql.OrderType;
import org.bees.cms.common.jpql.SimpleJPQL;
import org.bees.cms.core.Domain;
import org.bees.cms.service.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 公共的业务实现类<br/>
 * 这个公共业务实现类其实就是统一每个业务实现类都本应该具有的业务逻辑方法,就是从Service接口实现过来的方法<br/>
 * 所以当 每个业务接口都继承 Service 每个实现类都继承这个类,那么就能让每个实现类都有这些公共的功能,而且通过泛型,这些代码只有一份
 * 
 * @author 杨昌沛
 * 
 * @param <D>
 *            实体类
 * 
 * @since 1.0
 */
@Transactional
public abstract class ServiceSupport<D extends Domain> extends Generic<D> implements Service<D> {
	@PersistenceContext
	protected EntityManager manager;

	@Override
	public void persist(D domain) throws Exception {
		domain.persist();
	}

	@Override
	public void persist(List<D> domains) throws Exception {
		for (D domain : domains) {
			domain.persist();
		}
	}

	@Override
	public void merge(D domain) throws Exception {
		domain.merge();
	}

	@Override
	public void merge(List<D> domains) throws Exception {
		for (D domain : domains) {
			domain.merge();
		}
	}

	@Override
	public void remove(D domain) throws Exception {
		domain.delete();
	}

	@Override
	public void remove(List<D> domains) throws Exception {
		for (D domain : domains) {
			domain.delete();
		}
	}

	@Override
	public boolean exists(D domain) throws Exception {
		return domain != null && domain.exists();
	}

	@Override
	public boolean exists(List<D> domains) throws Exception {
		boolean exists = true;
		for (D domain : domains) {
			exists = exists && (domain != null && domain.exists());
			if (!exists) {
				break;
			}
		}
		return exists;
	}

	@Override
	public D find(Long id) throws Exception {
		return manager.find(parameter, id);
	}

	@Override
	public List<D> find(List<Long> ids) throws Exception {
		List<D> domains = new ArrayList<D>();
		for (Long id : ids) {
			domains.add(manager.find(parameter, id));
		}
		return domains;
	}

	@Override
	public D reference(Long id) throws Exception {
		return manager.getReference(parameter, id);
	}

	@Override
	public List<D> reference(List<Long> ids) throws Exception {
		List<D> domains = new ArrayList<D>();
		for (Long id : ids) {
			domains.add(manager.getReference(parameter, id));
		}
		return domains;
	}

	@Override
	public void refresh(D domain) throws Exception {
		domain.refresh();
	}

	@Override
	public void refresh(List<D> domains) throws Exception {
		for (D domain : domains) {
			domain.refresh();
		}
	}

	@Override
	public Page<D> paging(long current, long capacity, Criterion criterion, Order order, String by) throws Exception {
		SimpleJPQL jpql = new SimpleJPQL(parameter);
		if (criterion != null) {
			jpql.where().add(criterion);
		}
		if (order != null && by != null && !by.trim().equals("")) {
			jpql.order().by(by, OrderType.valueOf(order.name()));
		}
		jpql.limit().firstResult(Long.valueOf((current - 1) * capacity).intValue());
		jpql.limit().maxResult(Long.valueOf(capacity).intValue());
		List<D> domains = jpql.multiple(manager, parameter);

		jpql.limit().firstResult(0).maxResult(1);
		jpql.function().count("id");
		Long total = (Long) jpql.singleton(manager);

		Page<D> page = new Page<D>();
		page.setCurrent(current);
		page.setCapacity(capacity);
		page.setCondition(criterion == null ? "" : criterion.toString());

		page.setDomains(domains);
		page.setTotal(total);
		return page;
	}

	// 存在严重SQL注入问题...期待更好的解决方案...
	@SuppressWarnings("unchecked")
	@Override
	public Page<D> screen(long current, long capacity, String condition, Order order, String by) throws Exception {
		String WHERE = condition == null || condition.trim().equals("") ? "" : "WHERE " + condition + " ";

		String ORDER = order != null && by != null && !by.trim().equals("") ? "ORDER BY " + Mappers.getColumnName(parameter, by.trim()) + " " + order.name() : "";

		String sql4domains = "SELECT * FROM " + Mappers.getTableName(parameter) + " " + WHERE + ORDER;
		Query query = manager.createNativeQuery(sql4domains, parameter);
		query.setFirstResult(Long.valueOf((current - 1) * capacity).intValue());
		query.setMaxResults(Long.valueOf(capacity).intValue());
		List<D> domains = query.getResultList();

		String sql4total = "SELECT COUNT(*) FROM " + Mappers.getTableName(parameter) + " " + WHERE;
		query = manager.createNativeQuery(sql4total);
		BigInteger total = (BigInteger) query.getSingleResult();

		Page<D> page = new Page<D>();
		page.setCurrent(current);
		page.setCapacity(capacity);
		page.setCondition(condition);
		page.setOrder(order);
		page.setBy(by);
		page.setDomains(domains);
		page.setTotal(total.longValue());

		return page;
	}

	@Override
	public List<D> all() throws Exception {
		return new SimpleJPQL(parameter).multiple(manager, parameter);
	}

	@Override
	public void flush() throws Exception {
		manager.flush();
	}

	@Override
	public void clear() throws Exception {
		manager.clear();
	}

}
